package stream

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize/v2"
	"io/ioutil"
	"os"
	"unsafe"
)

type MyCell struct {
	StyleID int
	Value   interface{}
	Formula string
}

type myXlsxC struct {
	xlsxCRef
	MyF string `xml:"f,omitempty"`
}

type MyStreamWriter struct {
	*excelize.StreamWriter
	rawData myBufferedWriter
}

func NewMyStreamWriter(sw *excelize.StreamWriter) *MyStreamWriter {
	mySw := MyStreamWriter{
		StreamWriter: sw,
	}

	ref := new(StreamWriterRef)
	//获取sw的缓冲区地址
	bufRef := (*bytes.Buffer)(unsafe.Pointer(uintptr(unsafe.Pointer(sw)) + unsafe.Offsetof(ref.rawData) + unsafe.Offsetof(ref.rawData.buf)))
	//获取sw临时文件地址
	tmpRef := (**os.File)(unsafe.Pointer(uintptr(unsafe.Pointer(sw)) + unsafe.Offsetof(ref.rawData) + unsafe.Offsetof(ref.rawData.tmp)))
	if *tmpRef == nil {
		//创建临时文件
		*tmpRef, _ = ioutil.TempFile(os.TempDir(), "excelize-")
	}
	mySw.rawData.srcBufPoint = bufRef
	mySw.rawData.buf = *bufRef
	mySw.rawData.tmp = *tmpRef

	return &mySw
}

func (sw *MyStreamWriter) SetRow(axis string, values []interface{}) error {
	col, row, err := excelize.CellNameToCoordinates(axis)
	if err != nil {
		return err
	}

	fmt.Fprintf(&sw.rawData, `<row r="%d">`, row)
	for i, val := range values {
		axis, err := excelize.CoordinatesToCellName(col+i, row)
		if err != nil {
			return err
		}
		c := myXlsxC{xlsxCRef: xlsxCRef{R: axis}} /*改用自定义xlsxC结构*/
		if v, ok := val.(MyCell /*改用自定义Cell结构*/); ok {
			c.S = v.StyleID
			//给公式字段F赋值
			c.MyF = v.Formula
			val = v.Value
		} else if v, ok := val.(*MyCell /*改用自定义Cell结构*/); ok && v != nil {
			c.S = v.StyleID
			//给公式字段F赋值
			c.MyF = v.Formula
			val = v.Value
		}
		if err = setCellValFunc(&c.xlsxCRef, val); err != nil {
			sw.rawData.WriteString(`</row>`)
			return err
		}
		//使用改造过的函数
		writeMyCell(&sw.rawData, c)
	}
	sw.rawData.WriteString(`</row>`)
	return sw.rawData.Sync()
}

func writeMyCell(buf *myBufferedWriter, c myXlsxC) {
	buf.WriteString(`<c`)
	if c.XMLSpace.Value != "" {
		fmt.Fprintf(buf, ` xml:%s="%s"`, c.XMLSpace.Name.Local, c.XMLSpace.Value)
	}
	fmt.Fprintf(buf, ` r="%s"`, c.R)
	if c.S != 0 {
		fmt.Fprintf(buf, ` s="%d"`, c.S)
	}
	if c.T != "" {
		fmt.Fprintf(buf, ` t="%s"`, c.T)
	}
	buf.WriteString(`>`)
	/*
	 * 新增公式标签输出
	 */
	if c.MyF != "" {
		buf.WriteString(`<f>`)
		xml.EscapeText(buf, []byte(c.MyF))
		buf.WriteString(`</f>`)
	}
	if c.V != "" {
		buf.WriteString(`<v>`)
		xml.EscapeText(buf, []byte(c.V))
		buf.WriteString(`</v>`)
	}
	buf.WriteString(`</c>`)
}

type myBufferedWriter struct {
	bufferedWriterRef
	srcBufPoint *bytes.Buffer
}

func (buf *myBufferedWriter) Sync() error {
	err := buf.bufferedWriterRef.Sync()
	//同步缓冲区
	*buf.srcBufPoint = buf.buf
	return err
}
